#ifndef SHARED_PTR_H
#define SHARED_PTR_H
#include <iostream>
namespace lei {
class ResControlBlock {
 public:
  ResControlBlock(long use_count = 1) : use_count_(use_count) {}
  long GetUseCount() const { return use_count_; }
  void IncCount() { ++use_count_; }
  void DecCount() { --use_count_; }

 private:
  long use_count_;
};
template <typename T>
class shared_ptr {
 public:
  shared_ptr() = default;
  explicit shared_ptr(T *ptr = nullptr) : ptr_(ptr) {
    if (ptr)
      ctrl_ = new ResControlBlock;
    else
      ctrl_ = nullptr;
    std::cout << "shared_ptr construct, addr:" << this << std::endl;
  }
  // 实现强制类型转换需要的构造函数
  template <typename U>
  shared_ptr(const shared_ptr<U> &other, T *ptr) noexcept {
    ptr_ = ptr;
    if (ptr_) {
      other.ctrl_->IncCount();
      ctrl_ = other.ctrl_;
    }
    std::cout << "shared_ptr construct, addr:" << this << std::endl;
  }
  // 移动构造和拷贝构造
  shared_ptr(const shared_ptr<T> &other) noexcept {
    std::cout << "调用了拷贝构造!" << std::endl;
    ptr_ = other.ptr_;
    if (ptr_) {
      other.ctrl_->IncCount();
      ctrl_ = other.ctrl_;
    }
    std::cout << "shared_ptr construct, addr:" << this << std::endl;
  }
  shared_ptr(shared_ptr<T> &&other) noexcept {
    std::cout << "调用了移动构造!" << std::endl;
    ptr_ = other.ptr_;
    if (ptr_) {
      ctrl_ = other.ctrl_;
      other.ptr_ = nullptr;
      other.ctrl_ = nullptr;
    }
    std::cout << "shared_ptr construct, addr:" << this << std::endl;
  }
  // 带模板的拷贝与移动构造函数 模板的各个实例间并不天然就有 friend
  // 关系，因而不能互访私有成员 ptr_ 和 shared_count_。 需要下面显示声明
  template <typename U>
  friend class shared_ptr;
  template <typename U>
  shared_ptr(const shared_ptr<U> &other) noexcept {
    std::cout << "调用了带模板的拷贝构造!" << std::endl;
    ptr_ = other.ptr_;
    if (ptr_) {
      other.ctrl_->IncCount();
      ctrl_ = other.ctrl_;
    }
    std::cout << "shared_ptr construct, addr:" << this << std::endl;
  }
  template <typename U>
  shared_ptr(shared_ptr<U> &&other) noexcept {
    std::cout << "调用了带模板的移动构造!" << std::endl;
    ptr_ = other.ptr_;
    if (ptr_) {
      ctrl_ = other.ctrl_;
      other.ptr_ = nullptr;
      other.ctrl_ = nullptr;
    }
    std::cout << "shared_ptr construct, addr:" << this << std::endl;
  }
  // TODO:构造函数扩展
  ~shared_ptr() {
    ctrl_->DecCount();
    if (ctrl_->GetUseCount() == 0) {
      delete ctrl_;
      delete ptr_;
    }
    std::cout << "shared_ptr deconstruct, addr:" << this << std::endl;
  }

  void swap(const shared_ptr<T> &r) noexcept {
    swap(ptr_, r.ptr_);
    swap(ctrl_, r.ctrl_);
  }
  void reset(T *ptr = nullptr) noexcept { shared_ptr(ptr).swap(*this); }
  // TODO:reset其他类型sp
  T *get() const noexcept { return ptr_; }
  long use_count() const noexcept {
    if (ptr_)
      return ctrl_->GetUseCount();
    else
      return 0;
  }
  bool unique() const noexcept { return ctrl_->GetUseCount() == 1; }

  explicit operator bool() const noexcept { return ptr_; }
  T *operator->() const noexcept { return ptr_; }
  T &operator*() const noexcept { return *ptr_; }
  shared_ptr<T> &operator=(const shared_ptr<T> &r) noexcept {
    shared_ptr{r}.swap(*this);
    return *this;
  }
  shared_ptr<T> &operator=(shared_ptr<T> &&r) noexcept {
    shared_ptr{std::move(r)}.swap(*this);
    return *this;
  }

 private:
  T *ptr_;
  ResControlBlock *ctrl_;
};

template <typename T>
void swap(shared_ptr<T> &lhs, shared_ptr<T> &rhs) noexcept {
  lhs.swap(rhs);
}

template <typename T, typename U>
shared_ptr<T> dynamic_pointer_cast(const shared_ptr<U> &other) noexcept {
  T *ptr = dynamic_cast<T *>(other.get());
  return shared_ptr<T>(other, ptr);
}

template <typename T, typename U>
shared_ptr<T> static_pointer_cast(const shared_ptr<U> &other) noexcept {
  T *ptr = static_cast<T *>(other.get());
  return shared_ptr<T>(other, ptr);
}

template <typename T, typename U>
shared_ptr<T> const_pointer_cast(const shared_ptr<U> &other) noexcept {
  T *ptr = const_cast<T *>(other.get());
  return shared_ptr<T>(other, ptr);
}

template <typename T, typename U>
shared_ptr<T> reinterpret_pointer_cast(const shared_ptr<U> &other) noexcept {
  T *ptr = reinterpret_cast<T *>(other.get());
  return shared_ptr<T>(other, ptr);
}

template <typename T, typename... Args>
auto make_shared(Args &&...args) {
  return shared_ptr<T>{new T(std::forward<Args>(args)...)};
}
}  // namespace lei

#endif